---
title: .family
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

До того, как приступить к этому разделу, прочтите о [providers](/docs/concepts2/providers) и [как читать их](/docs/concepts/reading).
В этой части мы детально обсудим `.family`.

`.family` модификатор имеет единственное предназначение: создание уникального провайдера
с помощью внешних параметров.

Несколько типичных случаев применения `family`:

- Использование [FutureProvider] с `.family` для получения `Message` по ID
- Передача текущей `Locale` в провайдер для правильного перевода

## Использование

`.family` исполняет свое предназначение путем передачи дополнительного параметра
в провайдер.
Этот параметр можно использовать в провайдере для создания какого-либо состояния.

Например, мы можем использовать `family` с [FutureProvider] для получения
`Message` по ID:

```dart
final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
  return dio.get('http://my_api.dev/messages/$id');
});
```

Для работы с нашим `messagesFamily` синтаксис немного отличается.
Обычный синтаксис тут больше не работает.

```dart
Widget build(BuildContext context, WidgetRef ref) {
  // Ошибка: messagesFamily не является провайдером
  final response = ref.watch(messagesFamily);
}
```

Нам необходимо передать параметр в `messagesFamily`:

```dart
Widget build(BuildContext context, WidgetRef ref) {
  final response = ref.watch(messagesFamily('id'));
}
```

:::info
`family` можно использовать с разными аргументами одновременно.
Например, мы можем воспользоваться `titleFamily` для получения
французского и английского переводов в одно и то же время.

```dart
@override
Widget build(BuildContext context, WidgetRef ref) {
  final frenchTitle = ref.watch(titleFamily(const Locale('fr')));
  final englishTitle = ref.watch(titleFamily(const Locale('en')));

  return Text('fr: $frenchTitle en: $englishTitle');
}
```

:::

## Ограничения для параметров

Для исправной работы `family` важно указывать параметр с устойчивыми
`hashCode` и `==`.

В идеале параметр должен быть либо примитивным типом (bool/int/double/String),
либо константой (Provider), либо неизменяемым объектом, в котором переопределен
`==` и `hashCode`.

### _ПРЕДПОЧТИТЕЛЬНО_ использовать `autoDispose`, когда параметр не является константой:

Вам может прийти в голову идея использовать `family` для передачи поискового запроса
в провайдер. Но такое значение может изменяться очень часто и никогда не будет
переиспользовано.
Это может привести к утечке памяти, т.к. по умолчанию провайдер никогда не аннулируется,
даже если он больше не используется.

Совместное использование `.family` и `.autoDispose` решает проблему с утечкой памяти:

```dart
final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
  return fetchCharacters(filter: filter);
});
```

## Передача нескольких параметров в family

`family` не поддерживает передачу множества параметров в провайдер.

С другой стороны, значение может быть _любым_ (пока оно удовлетворяет ранее
упомянутым ограничениям).

Пример таких значений:

- tuple из [tuple](http://pub.dev/packages/tuple)
- Объекты, созданные с помощью [Freezed] или [built_value](https://pub.dev/packages/built_value)
- Объекты, использующие [equatable](https://pub.dev/packages/equatable)

Ниже представлен пример использования [Freezed] и [equatable] для работы
с множеством параметров:

<Tabs
  groupId="family"
  defaultValue="freezed"
  values={[
    { label: 'Freezed', value: 'freezed', },
    { label: 'Equatable', value: 'equatable', },
  ]}
>

<TabItem value="freezed">

```dart
@freezed
abstract class MyParameter with _$MyParameter {
  factory MyParameter({
    required int userId,
    required Locale locale,
  }) = _MyParameter;
}

final exampleProvider = Provider.autoDispose.family<Something, MyParameter>((ref, myParameter) {
  print(myParameter.userId);
  print(myParameter.locale);
  // Какие-то действия с userId/locale
});

@override
Widget build(BuildContext context, WidgetRef ref) {
  int userId; // Получение ID откуда-нибудь
  final locale = Localizations.localeOf(context);

  final something = ref.watch(
    exampleProvider(MyParameter(userId: userId, locale: locale)),
  );

  ...
}
```

</TabItem>
<TabItem value="equatable">

```dart
class MyParameter extends Equatable  {
  MyParameter({
    required this.userId,
    required this.locale,
  });

  final int userId;
  final Locale locale;

  @override
  List<Object> get props => [userId, locale];
}

final exampleProvider = Provider.family<Something, MyParameter>((ref, myParameter) {
  print(myParameter.userId);
  print(myParameter.locale);
  // Какие-то действия с userId/locale
});

@override
Widget build(BuildContext context, WidgetRef ref) {
  int userId; // Получение ID откуда-нибудь
  final locale = Localizations.localeOf(context);

  final something = ref.watch(
    exampleProvider(MyParameter(userId: userId, locale: locale)),
  );

  ...
}
```

</TabItem>
</Tabs>

[freezed]: https://pub.dev/packages/freezed
[equatable]: https://pub.dev/packages/equatable
[futureprovider]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/FutureProvider-class.html
